/**
 * @file main.c
 * @author Gvozdarev Sergey, gr016 MIPT
 * MIT license
 */
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include "mpi.h"

#define __DEBUG

#define G_ELEM_NUM 1
#define G_MAIN_RANK 0


#define real_t double
int main( int argc, char ** argv)  
{
    real_t tbegin, tend;
    
	int comm_rank,comm_size;
    
    int  mess_num;
    if ( argc != 2)
    {
        printf( "Number of message must be defined\n");
        return 1;
    }

    int NOK  =  sscanf( argv[ 1], "%d", &mess_num) != 1 || mess_num  < 1;

    if ( NOK)
    {
        printf( "Incorrect argument\n");
    }   
    MPI_Status status;

	//// Init
    MPI_Init( &argc, &argv ); 

	MPI_Comm_rank( MPI_COMM_WORLD, &comm_rank );
    MPI_Comm_size( MPI_COMM_WORLD, &comm_size );
    
    
    if ( comm_rank == 0) // main process
    {
        int i = comm_rank;
        tbegin = MPI_Wtime();
        
        printf  ( "%d: send %d\n", comm_rank, i);
        MPI_Ssend( &i, 1, MPI_INT, comm_rank + 1, G_ELEM_NUM, MPI_COMM_WORLD);

        while ( 1)
        {
            //printf( "%d: !r%d %d %d\n",comm_rank, mess_num - i, mess_num, i);

            if  ( ( mess_num - i) / comm_size >= 1)
            { 
                //printf  ( "%d: recv %d\n", comm_rank, i);
                MPI_Recv( &i, 1, MPI_INT, comm_size - 1, G_ELEM_NUM, MPI_COMM_WORLD, &status);
                i++;
            }
            else
            {
                break;
            }
            //
            //printf( "%d: !s%d %d %d\n",comm_rank, mess_num - i, mess_num, i);

            if ( ( ( mess_num - i) / comm_size >= 1) || ( ( mess_num - i) > 0))
            {
                printf  ( "%d: send %d\n", comm_rank, i);
                MPI_Ssend( &i, 1, MPI_INT, comm_rank + 1, G_ELEM_NUM, MPI_COMM_WORLD);
            }
            else
            {
                break;
            }
        }

        //printf( "%d:end\n",comm_rank);


        tend  = MPI_Wtime();
    }
    else // workers
    {
        int i = comm_rank;

        while ( 1)
        {       
            //printf( "%d: !r%d %d %d\n",comm_rank, mess_num - i, mess_num, i);

            if ( ( ( mess_num - i) / comm_size >= 1) || ( ( mess_num - i) >= 0))
            {
                //printf  ( "%d: recv %d\n", comm_rank, i);
                MPI_Recv( &i, 1, MPI_INT, comm_rank - 1, G_ELEM_NUM, MPI_COMM_WORLD, &status);
                i++;
            }
            else
            {
                break;
            }

            //

            //printf( "%d: !s%d %d %d\n",comm_rank ,mess_num - i, mess_num, i);
            if ( ( ( mess_num - i) / comm_size >= 1) || (( mess_num - i)  > 0))
            {
                printf  ( "%d: send %d\n", comm_rank, i);
                if ( comm_rank == comm_size -1)
                {
                    MPI_Ssend( &i, 1, MPI_INT, 0, G_ELEM_NUM, MPI_COMM_WORLD);
                }
                else
                {
                    MPI_Ssend( &i, 1, MPI_INT, comm_rank + 1, G_ELEM_NUM, MPI_COMM_WORLD);
                }
            }
            else
            {
                break;
            }
            i += comm_size;
        }

        //printf( "%d:end\n",comm_rank);
        
        MPI_Barrier( MPI_COMM_WORLD);
        MPI_Finalize();
        return 0;
    }
    

    
    MPI_Barrier( MPI_COMM_WORLD);
    printf("Result %d\n", (int)( 1000000*( tend - tbegin )));
    MPI_Finalize();
    return 0;
} 
